package com.framework.dubbo.producer.dubbo;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.framework.dubbo.producer.command.CommandHandler;
import com.framework.dubbo.producer.file2html.FilePreviewFactory;
import com.framework.dubbo.producer.file2html.FileType;
import com.framework.dubbo.producer.invoke.InvokeCommand;
import com.google.common.base.Charsets;
import com.framework.dubbo.producer.utils.StrUtils;
import com.framework.dubbo.DataFitUtil;
import com.framework.dubbo.FileSystemConfig;
import com.framework.dubbo.Pdf2HtmlService;
import com.framework.dubbo.producer.config.Constants;
import com.framework.dubbo.producer.config.Pdf2HtmlProperties;
import com.framework.dubbo.producer.utils.MinioUtils;
import com.framework.dubbo.producer.utils.PdfBoxUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import javax.annotation.Resource;
import java.io.File;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

/**
 * 描述：
 * dubbo远程服务调用接口
 * @author xianggj
 * @Date 2021/8/28 15:37
 **/
@Slf4j
@DubboService(version = "2.0.0")
public class DubboPdf2HtmlImpl implements Pdf2HtmlService {

    private static final String PDF = "pdf";

    /**
     * 和配置文件中文件名称对应
     */
    private static final String PDF_CSS = "pdf.css";

    @Resource
    private Pdf2HtmlProperties pdf2HtmlProperties;

    @Autowired
    @Qualifier("apacheExec")
    private InvokeCommand invokeCommand;


    /**
     * 同一个文件中心的转换
     * @param path
     * @param isFull
     * @return
     * @throws Exception
     */
    @Override
    public String pdf2html(FileSystemConfig fileSystemConfig, String path, String id, boolean isFull) throws Exception {
        //计时器
        TimeInterval timer = DateUtil.timer();
        //minio操作类
        MinioUtils minioUtils = new MinioUtils(fileSystemConfig);
        //文件源名称处理
        String fileName = StrUtil.subAfter(path, Constants.SEPARATOR, true);
        fileName = StrUtil.removeAll(fileName, " ");
        String ext = StrUtil.subAfter(fileName,  Constants.DOT, true);

        //落地本地
        //获取数据流
        InputStream fileStream = getDataInputStream(minioUtils, path);
        //获取目标文件夹
        File dir = FileUtil.mkdir(FileUtil.file(pdf2HtmlProperties.getWorkDir(), id));
        // 写入本地工作目录 固定写成index.pdf 避免不必要的麻烦
        File localFile = FileUtil.writeFromStream(fileStream, FileUtil.file(dir,
                DataFitUtil.HTML_COMMON_NAME+ Constants.DOT + ext));

        // 如果不是pdf文件
        if (!isPdf(localFile)) {
            return other2html(fileName, localFile);
        }

        //转换后的文件名称
        String htmlFileName = DataFitUtil.HTML_COMMON_NAME + DataFitUtil.HTML_SUFFIX;
        String distDir = dir.getAbsolutePath();
//        String command = getCommandStr(isFull, localFile.getAbsolutePath(), htmlFileName, distDir);
        // 异步调用pdf2htmlEX命令行工具
        try {
//            //异步执行  必须等待
//            CountDownLatch latch = new CountDownLatch(1);
//            Map<String, Throwable> throwableMap =
//                    invokeCommand.invokeCommand(localFile, command, latch);
//            if (throwableMap.size() > 0 ){
//                latch.countDown();
//                throw new RuntimeException("pdf转换html出错了");
//            }
//            // 等待所有子进程结束
//            latch.await();
            String htmlPath = distDir + File.separator + htmlFileName;
//            //不存在说明转换失败
//            if (!FileUtil.exist(htmlPath)){
            //直接调用java 转换 稍微有瑕疵 但是速度快
                PdfBoxUtils.pdf2html(localFile.getPath(), htmlPath);
//            }

            // 将生成的文件统一处理一遍
            String content = file2strForLocal(id, htmlPath);
            //内容再写回去
            FileUtil.writeString(content, new File(htmlPath), Charsets.UTF_8);
            //静态文件copy
            FileUtil.copy(distDir, pdf2HtmlProperties.getSourceDir(), true);
            //上传所以文件到minio
            Map<String, String> pathMap = minioUtils.uploadRunningDirs(dir, id);
            log.info("文件[{}]转换一共耗时{}ms", fileName, timer.interval());
            return pathMap.get(htmlFileName);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            throw new RuntimeException("系统异常："+ throwable.getMessage());
        } finally {
            FileUtil.del(dir);
        }
    }

    /**
     * 自适应js代码
     */
    private static final String JS_SCRIPT= "<script>window.onload=function(){var ww = window.screen.width;var p = ww/1920*200;document.documentElement.setAttribute(\"style\",\"font-size:\"+p+\"%\");}</script>";
    /**
     * 统一处理文件内容(存本地提供访问服务)
     * @param id 文件唯一id
     */
    private String file2strForLocal(String id, String htmlPath) {
        String serviceUrl = Constants.getHttp(pdf2HtmlProperties.getServiceHost(), pdf2HtmlProperties.getServicePort())
                + pdf2HtmlProperties.getServiceUrl() + id + Constants.SEPARATOR;

        //获取html文件内容
        FileReader fileReader = new FileReader(htmlPath);
        String result = fileReader.readString();
        //路径转换 前端需要死路径
        result = StrUtil.replace(result, "src=\"", "src=\""+serviceUrl);
        result = StrUtil.replace(result, " href=\"", " href=\"" +serviceUrl);
        //添加前端自适应
        result = StrUtil.replace(result, "<div id=\"page-container\">",
                "<div id=\"page-container\"><div id=\"pf-main\" style=\"width: 800px;\">" );
        result = StrUtil.replace(result, "<div class=\"loading-indicator\">",
                "</div><div class=\"loading-indicator\">");

        //自适应尺寸
//        result = StrUtil.replace(result, "</head>", JS_SCRIPT + "</head>");
        return result;
    }

    /**
     * 统一处理文件内容 (存文件系统提供服务)
     * 这个文件需要在后面加一个?token= 跳过认证 不好弄 暂时不做
     * @param id 文件唯一id
     * @param htmlFileName 文件名称
     * @param distDir 目录
     */
    private String file2strForFileSystem(  MinioUtils minioUtils, String id, String htmlFileName, String distDir) {
        String serviceUrl = minioUtils.getDownLoadPrefix() + id + Constants.SEPARATOR;

        //获取html文件内容
        FileReader fileReader = new FileReader(distDir + File.separator + htmlFileName);
        String result = fileReader.readString();
        //路径转换 前端需要死路径
        result = StrUtil.replace(result, "src=\"", "src=\""+serviceUrl);
        result = StrUtil.replace(result, " href=\"", " href=\"" +serviceUrl);
        return result;
    }

    /**
     * 获取命令
     * @param isFull
     * @param filePath
     * @param htmlFileName
     * @param distDir
     * @return
     */
    private String getCommandStr(boolean isFull, String filePath, String htmlFileName, String distDir) {
        String command = null;
        if (isFull) {
            command = getFullCommand(filePath, htmlFileName, distDir);
        } else {
            command = CommandHandler.getInstance()
                    .assembleCommand(pdf2HtmlProperties, filePath, distDir, htmlFileName);
        }
        return command;
    }

    /**
     * 获取数据流
     * @param path
     * @return
     * @throws Exception
     */
    private InputStream getDataInputStream(MinioUtils minioUtils, String path) throws Exception {
        path = StringUtils.startsWith(path, Constants.SEPARATOR)?StringUtils.substring(path, 1):path;
        String bucketName = StringUtils.substringBefore(path, Constants.SEPARATOR);
        String objectPath = StringUtils.substringAfter(path, Constants.SEPARATOR);
        //不验证了 已经验证过了
        boolean fileExists =  minioUtils.docsObjectExist(bucketName, objectPath);
        if (!fileExists){
            throw new RuntimeException("源文件不存在");
        }
        return minioUtils.getFileStream(bucketName, objectPath);
    }

    /**
     * 单文件转换
     *  弃用 需要返回流 太占用网络资源
     * @param stream
     * @param fileName
     * @param isFull 默认为false 为true表示图片安装base64处理
     */
    @Override
    @Deprecated
    public String pdf2html(byte[] stream, String fileName, boolean isFull) throws Exception{

        //避免文件名称重复 唯一id
        String id = IdUtil.simpleUUID();
        //获取目标文件夹
        File dir = FileUtil.mkdir(FileUtil.file(pdf2HtmlProperties.getWorkDir(), id));
        //源文件名去除空格
        String originalFilename = StrUtil.removeAll(fileName, " ");
        String ext = StrUtil.subAfter(originalFilename, ".", true);
        // 写入本地工作目录 固定写成index.pdf 避免不必要的麻烦
        File localFile = FileUtil.writeBytes(stream, FileUtil.file(dir, "index"+ Constants.DOT + ext));
        String localFileName = StrUtil.subBefore(localFile.getName(), ".", true);
        // pdf转换走另一边
        if (!isPdf(localFile)) {
            return other2html(originalFilename, localFile);
        }
        //计时器
        TimeInterval timer = DateUtil.timer();
        String htmlFileName = localFileName + DataFitUtil.HTML_SUFFIX;
        String distDir = dir.getAbsolutePath();
        String command = getCommandStr(isFull, localFile.getAbsolutePath(), htmlFileName, distDir);
        // 异步调用pdf2htmlEX命令行工具
        String context = null;
        try {

            //异步执行  必须等待
            CountDownLatch latch = new CountDownLatch(1);
            Map<String, Throwable> throwableMap =
                    invokeCommand.invokeCommand(localFile, command, latch);
            if (throwableMap.size() > 0 ){
                latch.countDown();
                throw new RuntimeException("pdf转换html出错了");
            }
            // 等待所有子进程结束
            latch.await();
            context = file2strForLocal(id, distDir + File.separator +  localFileName);
            //静态文件copy
            FileUtil.copy(distDir, pdf2HtmlProperties.getSourceDir(), true);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            throw new RuntimeException("系统异常："+ throwable.getMessage());
        } finally {
            FileUtil.del(dir);
        }
        log.info("文件[{}]转换一共耗时{}ms", originalFilename, timer.interval());
        return context;
    }

    private String other2html(String originalFilename, File localFile) {
        //计时器
        TimeInterval timer = DateUtil.timer();
        String serviceUrl = Constants.getHttp(pdf2HtmlProperties.getServiceHost(), pdf2HtmlProperties.getServicePort())
                + pdf2HtmlProperties.getServiceUrl();
        FileType fileType = FileType.enumOf(originalFilename);
        //转成其他文件的处理
        String htmlContext = FilePreviewFactory
                .getToolByType(fileType)
                .setStaticUrl(serviceUrl)
                .setStaticPath(pdf2HtmlProperties.getSourceDir())
                .setOldFileName(originalFilename)
                .convert2Html(localFile, fileType.getValue());
        log.info("文件[{}]转换一共耗时{}ms", originalFilename, timer.interval());
        return htmlContext;
    }

    private String other2html(String originalFilename, InputStream inputStream) {
        //计时器
        TimeInterval timer = DateUtil.timer();
        String serviceUrl = Constants.getHttp(pdf2HtmlProperties.getServiceHost(), pdf2HtmlProperties.getServicePort())
                + pdf2HtmlProperties.getServiceUrl();
        FileType fileType = FileType.enumOf(originalFilename);
        //转成其他文件的处理
        String htmlContext = FilePreviewFactory
                .getToolByType(fileType)
                .setStaticUrl(serviceUrl)
                .setStaticPath(pdf2HtmlProperties.getSourceDir())
                .setOldFileName(originalFilename)
                .convert2Html(inputStream, fileType.getValue());
        log.info("文件[{}]转换一共耗时{}ms", originalFilename, timer.interval());
        return htmlContext;
    }

    /**
     * 获取命令 <p>不分解图片文件</p>
     * @param filePath
     * @param htmlFileName
     * @param distDir
     * @return
     */
    private String getFullCommand(String filePath, String htmlFileName, String distDir) {
        String command = CommandHandler.getInstance()
                .assembleCommand(pdf2HtmlProperties, filePath, distDir, htmlFileName);
        command = StrUtil.subBefore(command, "woff", true)+ "woff";
        return command;
    }


    /**
     * 是否是pdf文件
     * @param file
     * @return
     */
    public static boolean isPdf(File file) {
        try {
            String localFileExt = StrUtil.subAfter(file.getName(), ".", true);
            return PDF.equalsIgnoreCase(FileTypeUtil.getType(file)) && PDF.equalsIgnoreCase(localFileExt);
        } catch (Exception e) {
            log.error("识别pdf类型失败, 文件名:{}, error: {}", file.getAbsolutePath(), e);
            return false;
        }
    }

    @Override
    public String pdf2Str(FileSystemConfig fileSystemConfig, String path, boolean isFormat) throws Exception {
        //计时器
        TimeInterval timer = DateUtil.timer();
        //minio操作类
        MinioUtils minioUtils = new MinioUtils(fileSystemConfig);
        //落地本地
        //获取数据流
        InputStream fileStream = getDataInputStream(minioUtils, path);
        //文件源名称处理
        String fileName = StrUtil.subAfter(path, Constants.SEPARATOR, true);

        FileType fileType = FileType.enumOf(fileName);

        //转成其他文件的处理
        String resStr = FilePreviewFactory
                .getToolByType(fileType)
                .convert2Str(fileStream, fileType.getValue());
        //是否需要格式
        if (isFormat){
            resStr = StrUtils.formatStr(resStr);
        }

        log.info("文件[{}]转换一共耗时{}ms", fileName, timer.interval());
        return resStr;
    }

//    public static void main(String[] args) throws Exception {
//        FileSystemConfig fileSystemConfig = new FileSystemConfig();
//        fileSystemConfig.setEndpoint("http://192.168.1.37:9000");
//        fileSystemConfig.setAccessKey("admin");
//        fileSystemConfig.setSecretKey("admin123456");
//
//        String s = new DubboPdf2HtmlImpl().pdf2Str(fileSystemConfig, "/kcs/kcs_data/0b6ef24f72054dc2a6f90326422c914e.pdf", false);
//        System.out.println(s);
//    }
}
